#include "NetworkCore.h"
#include "NetworkBase.h"
#include "EmotionFilter.h"
#include "ConfigImport.h"
#include "NetworkCreatTask.h"

#define LOGOUT
#define DEBUG
// #define GET_TIME
#define THREAD_POOL


PearsonCor::PearsonCor(ConfigImport* sys_config):
    sys_config(sys_config)
{
#ifdef LOGOUT
    std::cout << "use pearsonCor to creat network." << std::endl;
#endif

}
PearsonCor::~PearsonCor(){

}

/**********************************
 * 函数名：networkCreat
 * 功能：创建皮尔逊相关网络
 * 输入：单个trial的数据，矩阵形式
 * 输出：单个trial的网络行向量，去心上三角，纵向优先拉伸
 * 备注：无
**********************************/
arma::rowvec PearsonCor::networkCreat(arma::mat &inputData){
    // 滤波
    arma::mat filtMat(inputData.n_rows,inputData.n_cols);
    #if 1
    // 大系统原始滤波
    EmotionFilter emotionfilter = EmotionFilter(sys_config->getMapVal("EmotionFilterPara"));
    emotionfilter.filt(inputData,filtMat);
    #else
    
    #endif

    // cor方法计算的是矩阵的列之间的相关性,所以要先转置,这里要找出行之间的相关性
    arma::mat corNet = arma::cor(filtMat.t());

    // 将脑网络拉伸成一个行向量
    arma::mat unitUpperTri = arma::ones<arma::mat>(corNet.n_rows, corNet.n_rows);
    // 将下三角部分置为 0
    unitUpperTri = arma::trimatu(unitUpperTri);
    unitUpperTri.diag().zeros();
    arma::mat resMat = corNet(arma::find(unitUpperTri));
    arma::rowvec rowVec = arma::vectorise(resMat).t();
    return rowVec;
}



/*============================  网络相关  ==========================================*/
NetworkCore::NetworkCore(QObject* parent):
    QObject(parent),
    sys_config(ConfigImport::getInstance())
{
    netFunPtr = new PearsonCor(sys_config);
    networkReady = false;

}

NetworkCore::~NetworkCore(){
    if(netFunPtr){
        delete netFunPtr;
        netFunPtr = nullptr;
    }
    sys_config = nullptr;
}

/**********************************
 * 函数名：network_created
 * 功能： 创建一个网络矩阵
 * 输入：inputData:原始数据
 * 输出：无
 * 备注：无
**********************************/
void NetworkCore::network_created(const GenEEGData &inputData){
    // 根据输入的数据长度判断是训练的还是测试的数据，大于一个trial的是训练数据
    // 数据不满一个trial，舍弃
    if(inputData.data.size() < sys_config->pointPerTrial) {
        static int erroTimes = 0;
        ++erroTimes;
        std::cout << "In NetworkCore::network_created(), lost data times:" << erroTimes << std::endl;
        std::cout << "len:" << inputData.data.size() << std::endl;
        return;
    }
    // 传入训练的数据
    if(inputData.data.size() > sys_config->pointPerTrial){
        return;
    }

    // 传入一个trial的点数，实时显示脑网络或者预测
    if(inputData.data.size() == sys_config->pointPerTrial){
        #ifdef GET_TIME
        auto BT_creatNetwork = std::chrono::steady_clock::now();
        #endif // GET_TIME
        arma::mat oneTrial_origin(inputData.dataHead.channalNum, inputData.data.size());
        
        for (size_t i = 0; i < inputData.data.size(); ++i) {
            double* target_ptr = oneTrial_origin.colptr(i);
            std::memcpy(target_ptr, inputData.data[i].data(), EEGDataArr().size() * sizeof(double));
        }
        
        #ifdef THREAD_POOL
        NetworkCreatTask* task = new NetworkCreatTask(oneTrial_origin, netFunPtr);
        connect(task, &NetworkCreatTask::send_singleTrialNetwork, this, [=](arma::rowvec network){
            // static int times = 0;
            // ++times;
            // send_singleTrialNetwork(network);
            this->network = network;
            emit send_network(this->network);
            networkReady = true;
            // std::cout << "network:" << this->network.n_elem << std::endl;
            // std::cout << "NetworkCore::network_created,times:" << times << std::endl;
        });
        QThreadPool::globalInstance()->start(task);

        #else
        arma::rowvec rowVec = netFunPtr->networkCreat(oneTrial_origin);
        emit send_singleTrialNetwork(rowVec);
        #endif
        // #ifdef DEBUG
        // // rowVec.print("predict rowVec:");
        // std::cout << "predict rowVec:" << rowVec.n_rows << "x" << rowVec.n_cols << std::endl;
        // #endif // DEBUG
        // emit send_singleTrialNetwork(rowVec);

        #ifdef GET_TIME
        auto AT_creatNetwork = std::chrono::steady_clock::now();
        double duration_millsecond = std::chrono::duration<double, std::milli>(AT_creatNetwork - BT_creatNetwork).count();
        std::cout << "predict network Creat time:" << duration_millsecond << "ms" << std::endl; 
        #endif

        return;
    }
}

arma::rowvec NetworkCore::get_network(){
    if(!networkReady){
        return arma::rowvec();
    }
    networkReady = false;
    return network;
}





